import json
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, \
    QPushButton, QComboBox, QTextEdit, QListWidget, QListWidgetItem, QScrollBar, QGridLayout, QDoubleSpinBox, QSpinBox, \
    QFileDialog

from sllurp.gui import readerClasses
from sllurp.reader import R420


class InventoryApp(QMainWindow):
    def __init__(self):
        super().__init__()

        self.reader = None  # 读取器对象
        self.tags = []  # 存储读取到的标签列表
        self.trackedTags = []  # 存储跟踪的标签列表
        headFont = QFont("Arial")
        # 设置窗口标题
        self.setWindowTitle("Tag inventory")

        # 创建主窗口部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        # 创建布局
        layout = QHBoxLayout()
        main_widget.setLayout(layout)

        # 创建读取器配置面板
        self.conf_widget = QWidget()
        conf_layout = QVBoxLayout()
        self.conf_widget.setLayout(conf_layout)
        layout.addWidget(self.conf_widget)

        # 标题
        label_title = QLabel("Reader settings")
        label_title.setFont(headFont)  # 使用QFont设置字体
        conf_layout.addWidget(label_title)

        # IP地址
        self.ip_label = QLabel("IP address")
        self.ip_edit = QLineEdit()
        self.ip_edit.setText('192.168.5.2')
        conf_layout.addWidget(self.ip_label)
        conf_layout.addWidget(self.ip_edit)

        # 读取器型号
        self.model_label = QLabel("Reader model")
        self.model_combo = QComboBox()
        self.model_combo.addItems(readerClasses.keys())
        self.model_combo.setCurrentText('Generic')
        conf_layout.addWidget(self.model_label)
        conf_layout.addWidget(self.model_combo)

        # 连接按钮
        self.btnConnect = QPushButton("Connect reader")
        self.btnConnect.clicked.connect(self.connect)
        conf_layout.addWidget(self.btnConnect)

        # 创建找到标签列表
        self.taglist_widget = QWidget()
        taglist_layout = QVBoxLayout()
        self.taglist_widget.setLayout(taglist_layout)
        layout.addWidget(self.taglist_widget)

        # 标题
        self.tagsHeader = QLabel("Tags detected")
        self.tagsHeader.setFont(headFont)
        taglist_layout.addWidget(self.tagsHeader)

        # 包含检测到标签的列表
        self.tagsDetected = QListWidget()
        self.tagsDetected.clicked.connect(self.selectTag)
        taglist_layout.addWidget(self.tagsDetected)

        # 用于过滤标签的按钮
        self.trackBtn = QPushButton("Track tags")
        self.trackBtn.clicked.connect(self.trackTags)
        taglist_layout.addWidget(self.trackBtn)

        self.clearBtn = QPushButton("Clear tracked")
        self.clearBtn.clicked.connect(self.clearTrackedTags)
        taglist_layout.addWidget(self.clearBtn)

        # 创建所选标签信息面板
        self.taginfo_widget = QWidget()
        taginfo_layout = QVBoxLayout()
        self.taginfo_widget.setLayout(taginfo_layout)
        layout.addWidget(self.taginfo_widget)

        # 标题
        taginfo_layout.addWidget(QLabel("Tag info", font=headFont))

        # 标签信息字符串
        self.tagInfo = QTextEdit()
        self.tagInfo.setReadOnly(True)
        taginfo_layout.addWidget(self.tagInfo)

        self.show()

    def connect(self):
        # 构建设置
        # self.buildSettings()

        if not self.reader:
            try:
                reader_class = readerClasses[self.model_combo.currentText()]
                self.reader = reader_class(self.ip_edit.text())
            except:
                pass
            else:
                self.buildSettings()
                self.btnConnect.setDisabled(True)

    def buildSettings(self):
        layout = QGridLayout()

        # 发射功率
        pows = self.reader.power_table
        self.power_spinbox = QDoubleSpinBox()
        self.power_spinbox.setRange(pows[0], pows[-1])
        self.power_spinbox.setValue(pows[-1])
        layout.addWidget(QLabel('Tx power [dBm]'), 0, 0)
        layout.addWidget(self.power_spinbox, 0, 1)

        # 频率
        self.freq_combobox = QComboBox()
        self.freq_combobox.addItems(map(str, self.reader.freq_table))
        self.freq_combobox.setCurrentText(str(self.reader.freq_table[0]))
        layout.addWidget(QLabel('Frequency [MHz]'), 1, 0)
        layout.addWidget(self.freq_combobox, 1, 1)

        # 预设模式
        modeID = self.reader.mode_table[0]
        self.presetmode_combobox = QComboBox()
        self.presetmode_combobox.addItems(map(str, self.reader.mode_table))
        self.presetmode_combobox.setCurrentText(str(modeID))
        self.presetmode_combobox.currentIndexChanged.connect(self.displayModeInfos)  # 将下拉菜单的indexChanged信号连接到显示模式信息的槽函数
        layout.addWidget(self.presetmode_combobox, 2, 1)
        layout.addWidget(QLabel('Preset mode'), 2, 0)

        # 预设模式信息
        self.modeInfo_label = QLabel()
        layout.addWidget(self.modeInfo_label, 3, 0, 1, 2)

        # 搜索模式
        if isinstance(self.reader, R420):
            self.searchmode_combobox = QComboBox()
            self.searchmode_combobox.addItems(['0', '1', '2', '3'])
            self.searchmode_combobox.setCurrentText('0')
            layout.addWidget(QLabel('Search mode'), 4, 0)
            layout.addWidget(self.searchmode_combobox, 4, 1)

        # 会话
        self.session_combobox = QComboBox()
        self.session_combobox.addItems(['0', '1', '2', '3'])
        self.session_combobox.setCurrentText('1')
        layout.addWidget(QLabel('Session'), 5, 0)
        layout.addWidget(self.session_combobox, 5, 1)

        # 标签种群
        self.population_spinbox = QSpinBox()
        self.population_spinbox.setValue(4)
        layout.addWidget(QLabel('Tag population'), 6, 0)
        layout.addWidget(self.population_spinbox, 6, 1)

        # 盘存持续时间
        self.duration_spinbox = QDoubleSpinBox()
        self.duration_spinbox.setValue(1.0)
        layout.addWidget(QLabel('Inventory duration [s]'), 7, 0)
        layout.addWidget(self.duration_spinbox, 7, 1)

        # 天线
        self.antennas_edit = QLineEdit()
        self.antennas_edit.setText('all')
        layout.addWidget(QLabel('Antennas'), 8, 0)
        layout.addWidget(self.antennas_edit, 8, 1)

        # 单次盘存按钮
        single_inventory_btn = QPushButton('Single inventory')
        single_inventory_btn.clicked.connect(self.singleInventory)
        layout.addWidget(single_inventory_btn, 9, 0, 1, 2)

        # 实时盘存按钮
        self.liveInventory_btn = QPushButton('Live inventory')
        self.liveInventory_btn.clicked.connect(self.liveInventory)
        layout.addWidget(self.liveInventory_btn, 10, 0, 1, 2)

        # 保存能力的按钮
        save_capabilities_btn = QPushButton('Save capabilities')
        save_capabilities_btn.clicked.connect(self.saveCapabilities)
        layout.addWidget(save_capabilities_btn, 11, 0, 1, 2)

        # 应用布局到 conf_widget
        self.conf_widget.setLayout(layout)

    def displayModeInfos(self):
        modeID = self.presetModeComboBox.currentText()  # 获取当前选择的预设模式
        if not self.reader:
            return

        modes = self.reader.capabilities['RegulatoryCapabilities']['UHFBandCapabilities']['UHFRFModeTable']
        modeInfos = next(v for v in modes.values() if v['ModeIdentifier'] == modeID)

        infos = ''

        # C: EPC HAG T&C一致性标志
        # 跳过，因为不感兴趣

        # M: 谱掩码指示器
        ms = {
            0: 'unknown',
            1: 'single reader',
            2: 'multi reader',
            3: 'dense reader'
        }
        m = ms[modeInfos['M']]
        infos += 'Spectral mask: {}\n'.format(m)

        # PIE: 数据1到数据0符号长度比例的1000倍（1.5...2.0）
        pie = modeInfos['PIE']
        infos += 'PIE ratio: {}\n'.format(pie / 1000)

        # Min/Step/Max Tari: Tari时间（数据0符号的持续时间）以ns为单位
        minTari, stepTari, maxTari = modeInfos['MinTari'], modeInfos['StepTari'], modeInfos['MaxTari']
        infos += 'Tari: '
        if maxTari != minTari:
            infos += '{}...{} ({} step) us\n'.format(minTari / 1000, maxTari / 1000, stepTari / 1000)
        else:
            infos += '{} us\n'.format(minTari / 1000)

        # R: DR值。与BDR一起定义TRcal（TRcal = DR/BDR）
        # 跳过，因为不感兴趣

        # BDR: 反向散射数据速率（bps），即标签频率除以调制因子
        blf = modeInfos['BDR']
        infos += 'Tag data rate: {} bps\n'.format(blf / 1000)

        # Mod: 调制/编码
        mods = {
            0: 'FM0',
            1: 'Miller2',
            2: 'Miller4',
            3: 'Miller8'
        }
        mod = mods[modeInfos['Mod']]
        infos += 'Tag modulation: {}'.format(mod)

        # 将信息显示在标签中
        self.modeInfoLabel.setText(infos)

    def getSettings(self):
        # 从字符串中解析天线端口
        antStr = self.antennas_edit.text()
        antennas = (0,) if 'all' in antStr else tuple(int(a) for a in antStr.split(','))

        # 使用设置进行盘存
        settings = {
            'powerDBm': self.power_spinbox.value(),
            'freqMHz': float(self.freq_combobox.currentText()),
            'mode': int(self.presetmode_combobox.currentText()),
            'duration': self.duration_spinbox.value(),
            'session': int(self.session_combobox.currentText()),
            'population': self.population_spinbox.value(),
            'antennas': antennas
        }
        if isinstance(self.reader, R420):
            settings.update({'searchmode': int(self.searchmode_combobox.currentText())})

        return settings

    def singleInventory(self):
        if not self.reader:
            return

        settings = self.getSettings()
        self.tags = self.reader.detectTags(**settings)
        self.listTags()

    def liveInventory(self):
        if not self.reader:
            return

        if self.liveInventory_btn.text() == 'Live inventory':
            # 开始实时盘存
            settings = self.getSettings()
            settings['timeInterval'] = settings.pop('duration')
            settings['tagInterval'] = None
            self.reader.startLiveReports(self.collectTags, **settings)
            self.liveInventory_btn.setText('Stop inventory')
        else:
            # 停止实时盘存
            self.reader._liveStop.set()
            self.liveInventory_btn.setText('Live inventory')

    def listTags(self):
        # 清空标签列表
        self.tagsHeader.setText('')  # 清空摘要
        self.tagInfo.setText('')  # 清空所选标签信息
        self.tagsDetected.clear()  # 清空列表

        if not (self.reader and self.tags):
            return

        # 将找到的标签插入列表
        self.tagsHeader.setText('{} Tags ({} unique)'.format(len(self.tags), len(self.reader.uniqueTags(self.tags))))
        for tag in self.tags:
            rssi = tag.get('RSSI', tag.get('PeakRSSI'))
            # 制作RSSI的指示器
            if rssi >= -40:
                stars = '*****'
            elif rssi >= -50:
                stars = '****'
            elif rssi >= -60:
                stars = '***'
            elif rssi >= -70:
                stars = '**'
            else:
                stars = '*'
            # 获取标签ID的最后3位数字
            epc = self.reader.getEPC(tag)
            id = int(epc[-2:], 16)
            # 插入包含EPC、ID和RSSI的行
            self.tagsDetected.addItem('{} (...{}) | {}'.format(epc, id, stars))

    def collectTags(self, tag):
        # 收集实时盘存的标签
        self.tags.append(tag)
        self.listTags()

    def saveCapabilities(self):
        filepath, _ = QFileDialog.getSaveFileName(
            self,
            "Save Capabilities",
            "",
            "JSON Files (*.json);;All Files (*)"
        )
        if filepath and self.reader:
            with open(filepath, 'w') as file:
                file.write(json.dumps(self.reader.capabilities, indent=4))

    def trackTags(self):
        if self.reader:
            self.reader.includeEPCs = [self.reader.getEPC(tag) for tag in self.tags]

    def clearTrackedTags(self):
        if self.reader:
            self.reader.includeEPCs = []

    def selectTag(self):
        selected_tag = self.tagsDetected.currentItem()
        if selected_tag:
            epc = selected_tag.text().split()[0]
            tag_info = self.reader.getTagInfo(epc)
            if tag_info:
                info_text = ""
                for key, value in tag_info.items():
                    info_text += f"{key}: {value}\n"
                self.tagInfo.setPlainText(info_text)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = InventoryApp()
    sys.exit(app.exec())
